﻿/* 
 * Rug.Cmd part of Rugland Console Framework
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Copyright (C) 2008 Phill Tew. All rights reserved.
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Rug.Cmd.Gui
{
    public enum ConsoleProgressBarTextAlignment { None = 0, Center, Left, Right, Above, Below }
    public enum ConsoleProgressBarTextFormat { Value, ValueOfMax, Percent, Decimal }
    public enum ConsoleProgressBarCaps { Brackets, GreaterThanLessThan, SqrBrackets, Arrows, BlocksSeperated, BlocksJoined, Blocks, Clear }
 
    public class ConsoleProgressBar
    {
        #region Private Members
        
        private Point m_Location = new Point(0, 0);        

        #endregion

        #region Public Fields and Properties

        #region Public Color Fields

        public ConsoleColorExt ForeColor = ConsoleColorExt.Inhreit;
        public ConsoleColorExt BackColor = ConsoleColorExt.Inhreit;

        public ConsoleColorExt BarDimForeColor = ConsoleColorExt.Inhreit;
        public ConsoleColorExt BarDimBackColor = ConsoleColorExt.Inhreit;

        public ConsoleColorExt BarLitForeColor = ConsoleColorExt.Inhreit;
        public ConsoleColorExt BarLitBackColor = ConsoleColorExt.Inhreit;

        #endregion 

        #region Bar Shading Chars

        public ConsoleShade BarLitShade = ConsoleShade.Clear;
        public ConsoleShade BarDimShade = ConsoleShade.Dim;

        #endregion

        #region Bar Ending Caps

        public ConsoleProgressBarCaps Caps = ConsoleProgressBarCaps.BlocksJoined;

        #endregion 

        #region Display Fields

        public int TextPadding = 0;

        public string Message = ""; 

        public int Value = 0;
        public int Maximum = 1;

        public int Width = 80; 

        public string CustomFormat = "{0}";
        
        #endregion

        #region Text Alignment

        public ConsoleProgressBarTextAlignment TextAlignment = ConsoleProgressBarTextAlignment.Right;
        public ConsoleProgressBarTextFormat TextFormat = ConsoleProgressBarTextFormat.Percent;

        #endregion

        #region Location

        public Point Location
        {
            get { return m_Location; }
            set
            {
                m_Location = value;

                if (m_Location.X < 0 || m_Location.Y < 0)
                    throw new Exception(Strings.ConsoleControl_CtrlOutOfBounds);
            }
        }

        #endregion

        #endregion

        #region Render

        /// <summary>
        /// Render the bar
        /// </summary>
        public void Render()
        {
            if (Maximum < 1)
            {
                return;
            }

            string labelText = null;

            #region Label Text

            if (TextAlignment != ConsoleProgressBarTextAlignment.None)
            {
                string innerText = "";

                #region Pad the text

                if (TextPadding > 0)
                {
                    innerText = new string(' ', TextPadding);
                }

                #endregion

                #region Format the inner text

                switch (TextFormat)
                {
                    case ConsoleProgressBarTextFormat.Value:
                        innerText += Value.ToString().PadLeft(Maximum.ToString().Length, ' ');
                        break;
                    case ConsoleProgressBarTextFormat.ValueOfMax:
                        innerText += string.Format(Strings.ConsoleProgressBar_ValueOfMax, Value.ToString().PadLeft(Maximum.ToString().Length, ' '), Maximum.ToString());
                        break;
                    case ConsoleProgressBarTextFormat.Percent:
                        double percent = (100 / (double)Maximum) * Value;

                        innerText += percent.ToString("N1").PadLeft(5, ' ') + "%";
                        break;
                    case ConsoleProgressBarTextFormat.Decimal:
                        double @decimal = (1 / (double)Maximum) * Value;

                        innerText += @decimal.ToString("N3").PadLeft(Maximum.ToString("N3").Length, ' ');
                        break;
                    default:
                        break;
                }

                #endregion 

                labelText = string.Format(CustomFormat, innerText);
            }

            #endregion

            int labelLength = labelText != null ? labelText.Length : 0;           
            int barTotalInt = Width - (2 + labelLength);
            double barTotal = (double)barTotalInt; 

            double position = (barTotal / Maximum) * Value;

            int index = (int)position;

            if (index > barTotalInt)
            {
                index = barTotalInt;
            }

            ConsoleColorState state = RC.ColorState; 

            RC.SetCursorPosition(this.Location.X, this.Location.Y);

            RC.ForegroundColor = ForeColor;
            RC.BackgroundColor = BackColor;

            WriteEndCap(true, Caps);           

            string TrimmedMessage = Message.Length > barTotalInt ? Message.Substring(0, barTotalInt) : Message;

            if (index > 0)
            {
                RC.ForegroundColor = BarLitForeColor;
                RC.BackgroundColor = BarLitBackColor;

                if (TrimmedMessage.Length < index)
                {
                    RC.Write(TrimmedMessage);

                    RC.Write(new string(ConsoleChars.GetShade(BarLitShade), index - TrimmedMessage.Length));
                }
                else if (TrimmedMessage.Length == index)
                {
                    RC.Write(TrimmedMessage);
                }
                else if (TrimmedMessage.Length > index)
                {
                    RC.Write(TrimmedMessage.Substring(0, index));
                }

                if (index < barTotalInt)
                {
                    RC.ForegroundColor = BarDimForeColor;
                    RC.BackgroundColor = BarDimBackColor;

                    int remains = barTotalInt - index;

                    if (TrimmedMessage.Length <= index)
                    {
                        RC.Write(new string(ConsoleChars.GetShade(BarDimShade), remains));
                    }
                    else if (TrimmedMessage.Length > index)
                    {
                        string end = TrimmedMessage.Substring(index);

                        RC.Write(end);

                        RC.Write(new string(ConsoleChars.GetShade(BarDimShade), remains - end.Length));
                    }
                }
            }
            else 
            {
                RC.ForegroundColor = BarDimForeColor;
                RC.BackgroundColor = BarDimBackColor;

                RC.Write(TrimmedMessage);

                RC.Write(new string(ConsoleChars.GetShade(BarDimShade), barTotalInt - TrimmedMessage.Length));
            }

            RC.ForegroundColor = ForeColor;
            RC.BackgroundColor = BackColor;

            WriteEndCap(false, Caps);

            if (TextAlignment != ConsoleProgressBarTextAlignment.None)
            {
                // calcuate the real position
                RC.SetCursorPosition(this.Location.X + (Width - labelLength), this.Location.Y);

                RC.ForegroundColor = ForeColor;
                RC.BackgroundColor = BackColor;

                RC.Write(labelText);
            }

            RC.ColorState = state; 
        }

        /// <summary>
        /// Write an end cap char
        /// </summary>
        /// <param name="leftSide">true if for the left side of the bar</param>
        /// <param name="caps">the bar cap type</param>
        private void WriteEndCap(bool leftSide, ConsoleProgressBarCaps caps)
        {
            if (leftSide == true)
            {
                switch (Caps)
                {
                    case ConsoleProgressBarCaps.Brackets:
                        RC.Write("(");
                        break;
                    case ConsoleProgressBarCaps.GreaterThanLessThan:
                        RC.Write("<");
                        break;
                    case ConsoleProgressBarCaps.SqrBrackets:
                        RC.Write("[");
                        break;
                    case ConsoleProgressBarCaps.Arrows:
                        RC.Write(ConsoleChars.GetArrow(ConsoleArrows.Left).ToString());
                        break;
                    case ConsoleProgressBarCaps.BlocksSeperated:
                        RC.Write(ConsoleChars.GetShade(ConsoleShade.HalfLeft).ToString());
                        break;
                    case ConsoleProgressBarCaps.BlocksJoined:
                        RC.Write(ConsoleChars.GetShade(ConsoleShade.HalfRight).ToString());
                        break;
                    case ConsoleProgressBarCaps.Blocks:
                        RC.Write(ConsoleChars.GetShade(ConsoleShade.Opaque).ToString());
                        break;
                    case ConsoleProgressBarCaps.Clear:
                        RC.Write(" ");
                        break;
                    default:
                        break;
                }
            }
            else
            {
                switch (Caps)
                {
                    case ConsoleProgressBarCaps.Brackets:
                        RC.Write(")");
                        break;
                    case ConsoleProgressBarCaps.GreaterThanLessThan:
                        RC.Write(">");
                        break;
                    case ConsoleProgressBarCaps.SqrBrackets:
                        RC.Write("]");
                        break;
                    case ConsoleProgressBarCaps.Arrows:
                        RC.Write(ConsoleChars.GetArrow(ConsoleArrows.Right).ToString());
                        break;
                    case ConsoleProgressBarCaps.BlocksSeperated:
                        RC.Write(ConsoleChars.GetShade(ConsoleShade.HalfRight).ToString());
                        break;
                    case ConsoleProgressBarCaps.BlocksJoined:
                        RC.Write(ConsoleChars.GetShade(ConsoleShade.HalfLeft).ToString());
                        break;
                    case ConsoleProgressBarCaps.Blocks:
                        RC.Write(ConsoleChars.GetShade(ConsoleShade.Opaque).ToString());
                        break;
                    case ConsoleProgressBarCaps.Clear:
                        RC.Write(" ");
                        break;
                    default:
                        break;
                }
            }
        }

        #endregion
    }
}
